﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Xml.Serialization;

namespace Objects
{
    public class Validator<T> : Validator
    {
        protected Validator() : base() { }
        private Validator(Func<T, ValidationResult> validateFunc)
            : this()
        {
            this.validateFunc = validateFunc;
        }
        private Func<T, ValidationResult> validateFunc;
        public override bool Validate(IValidable obj, bool Recurse)
        {
            return base.Validate(obj, Recurse) & ValidateT(obj as Validable<T>, Recurse);
        }
        protected virtual bool ValidateT(Validable<T> obj, bool Recurse)
        {
            bool res = true;
            if (obj != null)
            {
                ValidationResult tmp;
                IValidable recurse;
                if (Recurse && (recurse = obj.SpecValue as IValidable) != null && recurse.Validate(Recurse))
                    foreach (var item in recurse.DescriptionValid)
                        obj.AddValidationResult(item);
                if (validateFunc != null)
                    foreach (Func<T, ValidationResult> item in validateFunc.GetInvocationList())
                    {
                        tmp = item(obj.SpecValue) ?? ValidationResult.Null;
                        res = res & tmp.IsSuccess;
                        obj.AddValidationResult(tmp);
                    }
            }
            return res;
        }
        public static Validator<T> Create(Func<T, ValidationResult> validateFunc)
        {
            if (validateFunc != null)
                return new Validator<T>(validateFunc);
            else
                return null;
        }
    }

    public class Validator<T, Tstate> : Validator<T>
    {
        protected Validator() : base() { }
        private Validator(Tstate state, Func<Tstate, T, ValidationResult> validateFunc)
            : this()
        {
            this.validateFunc = validateFunc;
            this.State = state;
        }
        public Tstate State;
        private Func<Tstate, T, ValidationResult> validateFunc;
        protected override bool ValidateT(Validable<T> obj, bool Recurse)
        {
            bool res = true;
            if (obj != null)
            {
                ValidationResult tmp;
                if (validateFunc != null)
                    foreach (Func<Tstate, T, ValidationResult> item in validateFunc.GetInvocationList())
                    {
                        tmp = item(State, obj.SpecValue) ?? ValidationResult.Null;
                        res = res & tmp.IsSuccess;
                        obj.AddValidationResult(tmp);
                    }
            }
            return res;
        }
        public static Validator<T, Tstate> Create(Tstate state, Func<Tstate, T, ValidationResult> validateFunc)
        {
            if (validateFunc != null)
                return new Validator<T, Tstate>(state, validateFunc);
            else
                return null;
        }
    }

    public class Validator
    {
        private Func<IValidable, ValidationResult> validateFunc;
        public virtual bool Validate(IValidable obj, bool Recurse)
        {
            bool res = true;
            ValidationResult tmp;
            if (validateFunc != null)
                foreach (Func<IValidable, ValidationResult> item in validateFunc.GetInvocationList())
                {
                    tmp = item(obj) ?? ValidationResult.Null;
                    res = res & tmp.IsSuccess;
                    obj.AddValidationResult(tmp);
                }
            return res;
        }
        #region Constructors
        protected Validator()
        {
        }
        private Validator(Func<IValidable, ValidationResult> validateFunc)
            : this()
        {
            this.validateFunc = validateFunc;
        }
        public static Validator Create(Func<IValidable, ValidationResult> validateFunc)
        {
            if (validateFunc != null)
                return new Validator(validateFunc);
            else
                return null;
        }
        #endregion

        #region Templates
        /// <summary>
        /// Проверка внутренних полей
        /// </summary>
        public static readonly Validator InnerFields = Validator.Create((s) =>
        {
            if (s != null)
            {
                ValidationResultList lst = new ValidationResultList();
                IValidable tmp;
                foreach (var item in s
                                        .GetType()
                                        .GetPropertiesWithAttribute<FieldAttribute>()
                                        .Where(t => t.PropertyType.IsSubclassOf(typeof(IValidable))))
                    if ((tmp = item.GetValue(s, null) as IValidable) != null
                            & tmp.Validate(true))
                        lst.Inner.Add(tmp.DescriptionValid);

                return lst;
            }
            else
                return ValidationResult.Null;
        });

        public static Validator<double> PositiveDouble
        {
            get
            {
                return Validator<double>.Create((s) =>
                {
                    if (s < 0)
                        return new ValidationResult() { IsSuccess = false, Description = "Значение должно быть не отрицательным" };
                    else
                        return ValidationResult.Null;
                });
            }
        }        

        public static Validator<int> PositiveInt
        {
            get
            {
                return Validator<int>.Create((s) =>
                {
                    if (s < 0)
                        return new ValidationResult() { IsSuccess = false, Description = "Значение должно быть не отрицательным" };
                    else
                        return ValidationResult.Null;
                });
            }
        }

        /// <summary>
        /// Строка ограниченная интервалом
        /// </summary>
        /// <param name="MinLength"></param>
        /// <param name="MaxLength"></param>
        /// <returns></returns>
        public static Validator<string> StringLimited(int MinLength, int MaxLength)
        {
            return Validator<string>.Create((s) =>
            {
                int len = s.GetLength();
                if (len < MinLength)
                    return new ValidationResult() { IsSuccess = false, Description = "Длина не должна быть меньше {0} символов".goFormat(MinLength) };
                else if (len > MaxLength)
                    return new ValidationResult() { IsSuccess = false, Description = "Длина не должна превышать {0} символов".goFormat(MaxLength) };
                return ValidationResult.Null;
            });
        }

        /// <summary>
        /// Строка ограниченная интервалом
        /// </summary>
        /// <param name="MaxLength"></param>
        /// <returns></returns>
        public static Validator<string> StringLimited(int MaxLength)
        {
            return Validator<string>.Create((s) =>
            {
                int len = s.GetLength();
                if (len > MaxLength)
                    return new ValidationResult() { IsSuccess = false, Description = "Длина не должна превышать {0}".goFormat(MaxLength) };
                return ValidationResult.Null;
            });
        }

        /// <summary>
        /// Строка со списком возможных длин
        /// </summary>
        /// <param name="lens"></param>
        /// <returns></returns>
        public static Validator<string> StringVariable(params int[] lens)
        {
            return Validator<string, int[]>.Create(lens, (s, l) =>
            {
                int len = l.GetLength();
                if (s != null && s.Length > 0 && !s.Contains(len))
                {
                    StringBuilder sb = new StringBuilder();
                    sb.Append(s[0]);
                    for (int i = 1; i < s.Length; i++)
                        sb.Append(", ").Append(s[i]);
                    return new ValidationResult() { IsSuccess = false, Description = "Длина должна быть из набора [{0}]".goFormat(sb) };
                }
                return ValidationResult.Null;
            });
        }
        #endregion

        public static Validator operator +(Validator valid1, Validator valid2)
        {
            return valid1.Combine(valid2);
        }

    }

    internal class ValidatorsList : Validator
    {
        internal readonly List<Validator> inner = new List<Validator>();
        public override bool Validate(IValidable obj, bool Recurse)
        {
            if (obj != null)
            {
                bool res = true;
                foreach (var item in inner)
                    res = res & item.Validate(obj, Recurse);
                return res;
            }
            return false;
                        
        }
    }

    public static class ValidatorExtension
    {
        public static Validator Combine(this Validator valid1, Validator valid2)
        {
            if (valid1 == null)
                return valid2;
            else if (valid2 == null)
                return valid1;
            else if (valid1 == valid2)
                return valid1;
            else
            {
                ValidatorsList lst, res = new ValidatorsList();
                if ((lst = valid1 as ValidatorsList) == null)
                    res.inner.Add(valid1);
                else
                    res.inner.AddRange(lst.inner);
                if ((lst = valid2 as ValidatorsList) == null)
                    res.inner.Add(valid2);
                else
                    res.inner.AddRange(lst.inner);
                return res;
            }

        }
    }
}
